﻿//Copyright 2010 http://tv4home.codeplex.com
//This file is part of TV4Home.WebInterface.UI.
//TV4Home.WebInterface.UI is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TV4Home.WebInterface.UI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TV4Home.WebInterface.UI If not, see http://www.gnu.org/licenses/
using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Web;
using TV4Home.WebInterface.UI.Code;

namespace TV4Home.WebInterface.UI.WebService
{
    public class WebServiceProtection : UserNamePasswordValidator
    {
        private static int falseRequestCount = 0;

        public override void Validate(string userName, string password)
        {
            // sleep to prevent brute-force attacks
            if (falseRequestCount > 0)
                System.Threading.Thread.Sleep(falseRequestCount * 100);

            if (userName == "TV4Home_WebService" && password == WebsiteSettings.WebServiceAccessCode)
            {
                // correct password
                falseRequestCount = 0;
                return;
            }

            // false password
            falseRequestCount++;
            throw new FaultException("Unknown username or incorrect password!");
        }
    }

    public class WebServiceJsonProtectionBehavior : IEndpointBehavior, IDispatchMessageInspector
    {
        private static WebServiceProtection protection = new WebServiceProtection();

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // retrieve the http request
            HttpRequestMessageProperty requestMessage = request.Properties["httpRequest"] as HttpRequestMessageProperty;

            if (requestMessage == null)
                throw new InvalidOperationException("HttpRequestMessageProperty 'httpRequest' property not found!");

            // process the 'Authorization' http header
            if (String.IsNullOrEmpty(requestMessage.Headers["Authorization"]))
                throw new InvalidOperationException("The 'Authorization' http-header is required!");

            // authenticate the user
            Authenticate(requestMessage.Headers["Authorization"]);

            return null;
        }

        private static void Authenticate(string authHeader)
        {
            // check if it's Basic auth
            if (!authHeader.StartsWith("Basic "))
                throw new InvalidOperationException("Only Basic Authentication is supported!");

            // strip out only the base64 encoded user/pw
            string base64UsernamePassword = authHeader.Remove(0, "Basic ".Length);

            // convert to standard ASCII
            string usernamePassword = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(base64UsernamePassword));

            // split into username and pw
            string[] usernamePasswordArray = usernamePassword.Split(new char[] { ':' }, 2);

            if (usernamePasswordArray.Length != 2)
                throw new InvalidOperationException("Incorrect 'Authorization' header!");

            // validate
            protection.Validate(usernamePasswordArray[0], usernamePasswordArray[1]);
        }

        #region Not Implemented Methods
        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
        #endregion
    }
}